/**
  ******************************************************************************
  * @file    xmodem.h
  * @author  ashuai0110
  * @version V2.3
  * @date    2024-07-01
  * @brief   xmodem协议
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module.git
  *
  ******************************************************************************
  */

#ifndef __XMODEM_H
#define __XMODEM_H

#ifdef __cplusplus
extern "C" {
#endif

/* 包含头文件-----------------------------------------------------------------*/
#include "common_include.h"

/**
 * @addtogroup bootloader
 * @{
 */

/**
 * @addtogroup xmodem
 * @{
 */

/* 宏定义---------------------------------------------------------------------*/
/**
 * @defgroup xmodem_global_macros xmodem global macros
 * @{
 */

/**
 * @defgroup xmodem_info xmodem info
 * @{
 */
/* /------------------xmodem协议格式------------------\
 * | 命令符 | 包序号 | 包序号反码 |  有效数据  | 校验 |
 * |   1    |   1    |      1     |  128/1024  |  1/2 |
 * \--------------------------------------------------/ */
#define XMODEM_START_INDEX              (0u)
#define XMODEM_NUMBER_INDEX             (1u)
#define XMODEM_CNUMBER_INDEX            (2u)
#define XMODEM_DATA_INDEX               (3u)
#define XMODEM_HEADER_SIZE              (3u)
#define XMODEM_DATA_SIZE                (128u)
#define XMODEM_DATA_1K_SIZE             (1024u)
/**
 * @}
 */

/**
 * @defgroup xmodem_command_symbol xmodem command symbol
 * @{
 */
#define XMODEM_SOH                      (0x01u) /*!< 命令符:128byte数据 */
#define XMODEM_STX                      (0x02u) /*!< 命令符:1024byte数据 */
#define XMODEM_EOT                      (0x04u) /*!< 命令符:发送结束 */
#define XMODEM_ACK                      (0x06u) /*!< 命令符:正确应答 */
#define XMODEM_NAK                      (0x15u) /*!< 命令符:错误应答/校验方式累加和 */
#define XMODEM_CAN                      (0x18u) /*!< 命令符:取消发送 */
#define XMODEM_CTRLZ                    (0x1Au) /*!< 填充字节 */
#define XMODEM_CRC16                    ('C')   /*!< 命令符:校验方式CRC16 */
/**
 * @}
 */

/**
 * @defgroup xmodem_rx_state xmodem rx state
 * @{
 */
#define XMODEM_STATE_IDLE               (0u) /*!< 空闲状态 */
#define XMODEM_STATE_WAIT               (1u) /*!< 等待接收状态 */
#define XMODEM_STATE_CHECK_HEAD         (2u) /*!< 校验数据头状态 */
#define XMODEM_STATE_CHECK_DATA         (3u) /*!< 校验数据状态 */
/**
 * @}
 */

/**
 * @defgroup xmodem_rx_flag xmodem rx flag
 * @{
 */
#define XMODEM_RX_FLAG_NAK              (XMODEM_NAK)   /*!< 接收方发送NAK启动传输,发送方使用累加和校验 */
#define XMODEM_RX_FLAG_CRC              (XMODEM_CRC16) /*!< 接收方发送'C'启动传输,发送方使用CRC16校验 */
/**
 * @}
 */

/**
 * @}
 */

/* 类型定义-------------------------------------------------------------------*/
/**
 * @defgroup xmodem_global_types xmodem global types
 * @{
 */

/**
  * @brief  xmodem发送前(串口)准备函数
  *
  * @note   如:使用串口中断发送时需使能发送相关中断/使用串口DMA发送时需配置使能DMA
  *
  * @param  enState : 0-关闭(失能)  1-开启(使能)
  *
  * @retval None
  */
typedef void (* xmodem_tx_en_t)(uint8_t enState);

/**
  * @brief  xmodem获取/设置数据包函数
  *
  * @note   获取数据包即本机接收对方传输来的数据, 设置数据包即本机提供将要发送的数据
  *
  * @param  packetNum : 从1开始,超出后回到0继续累加(1-255,0-255...)
  *
  * @param  data      : 获取数据包此参数包含有效数据/设置数据包此参数需用户写入有效数据
  *
  * @param  len       : 获取数据包此参数包含数据长度(包含填充数据)/设置数据包此参数需用户写入数据长度(不包含填充数据)
  *
  * @retval None
  */
typedef void (* xmodem_user_packet_cb_t)(uint8_t packetNum, uint8_t *data, uint16_t *len);

/** 
  * @brief  xmodem structure definition
  */
typedef struct xmodem {
    uint8_t *dataBuf;       /*!< 数据缓冲区 */
    uint16_t dataBufLen;    /*!< 数据缓冲区大小 */
    uint16_t dataLen;       /*!< 存放数据数量 */
    uint16_t readLen;       /*!< 数据读出数量 */
    uint16_t dataLenTmp;    /*!< 存放数据数量暂存 */
    
    uint16_t rspTimeout;    /*!< 回复超时时间ms */
    uint32_t rspTimeoutCnt; /*!< 回复超时时间计数 */
    uint8_t resendTimes;    /*!< 超时重发次数 */
    uint8_t resendTimesCnt; /*!< 超时重发计数 */
    uint8_t packetNum;      /*!< 包序号 */
    
    uint8_t state : 3;      /*!< xmodem状态 @ref xmodem_rx_state */
    uint8_t checkNum : 2;   /*!< 校验字节数 1:累加和 2:CRC16 */
    uint8_t eotFlag : 1;    /*!< 传输结束标志 0:未结束 1:传输结束 */
    uint8_t recFlag : 1;    /*!< 接收标志 0:未接收到 1:接收到 */
    
    xmodem_tx_en_t txEnFn;   /*!< 串口发送准备函数 */
    xmodem_user_packet_cb_t getPacketFn; /*!< 获取数据包回调函数 */
    xmodem_user_packet_cb_t setPacketFn; /*!< 设置数据包回调函数 */
} xmodem_t;

/**
 * @}
 */

/* 全局变量-------------------------------------------------------------------*/

/* 函数原型-------------------------------------------------------------------*/
/**
 * @addtogroup xmodem_global_functions
 * @{
 */

void xmodem_init(xmodem_t *_xmodem, void *dataBuf, uint16_t bufLen, xmodem_tx_en_t txEnFn, \
    xmodem_user_packet_cb_t getPacketFn, xmodem_user_packet_cb_t setPacketFn, uint16_t rspTimeout, uint8_t resendTimes);

uint8_t xmodem_rx_poll(xmodem_t *_xmodem);
uint8_t xmodem_tx_poll(xmodem_t *_xmodem);

void xmodem_rx_start(xmodem_t *_xmodem, uint8_t startFlag);

uint16_t xmodem_write_rx_buf(xmodem_t *_xmodem, void *pBuf, uint16_t len);
uint16_t xmodem_read_tx_buf(xmodem_t *_xmodem, void *pBuf, uint16_t len);

/* set/get interface */
void xmodem_set_rsp_timeout(xmodem_t *_xmodem, uint16_t rspTimeout);
uint16_t xmodem_get_rsp_timeout(xmodem_t *_xmodem);
void xmodem_set_resend_times(xmodem_t *_xmodem, uint8_t resendTimes);
uint8_t xmodem_get_resend_times(xmodem_t *_xmodem);

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif /* xmodem.h */
